package main

import (
	"fmt"
	"math/rand"
	"sort"
)

func main() {
	question1()
	fmt.Println(generateAdjacentSumSlice([]int{1, 4, 9, 16, 2, 5, 10, 15}))
	fmt.Println(duplicatesNumberCount(100, -100, 100))
	question4()
}

// 1、看下面一段程序，回答问题
func question1() {
	s1 := make([]int, 3, 4)
	s2 := append(s1, 1)

	// 请问s1、s2内各有什么元素？
	fmt.Println("请问s1、s2内各有什么元素？")
	// s1 []int len=3 cap=4 [0 0 0]
	fmt.Printf("%T len=%d cap=%d %[1]v\n", s1, len(s1), cap(s1))
	// s2 []int len=4 cap=4 [0 0 0 1]
	fmt.Printf("%T len=%d cap=%d %[1]v\n", s2, len(s2), cap(s2))
	fmt.Println("~~~~~~~~~~~~~~~~~~")

	// s1修改一个元素会影响s2吗？s2修改一个元素会影响s1吗？
	fmt.Println("s1修改一个元素会影响s2吗？s2修改一个元素会影响s1吗？")
	s1[0] = 4 // s1修改任意数据都影响s2
	s2[1] = 5 // s2修改下标<len(s1)的数据时，影响
	s2[3] = 6 // s2修改下标>=len(s1)的数据时，不影响
	// s1 []int len=3 cap=4 [4 5 0]
	fmt.Printf("%T len=%d cap=%d %p %p %[1]v\n", s1, len(s1), cap(s1), &s1, &s1[0])
	// s2 []int len=4 cap=4 [4 5 0 6]
	fmt.Printf("%T len=%d cap=%d %p %p %[1]v\n", s2, len(s2), cap(s2), &s2, &s2[0])
	fmt.Println("s1、s2使用的是一个底层数组？", &s1[0] == &s2[0])
	fmt.Println("~~~~~~~~~~~~~~~~~~")

	// s2再增加一个元素会怎么样？
	fmt.Println("s2再增加一个元素会怎么样？")
	s2 = append(s2, 7)
	// s1 []int len=3 cap=4 [4 5 0]
	fmt.Printf("%T len=%d cap=%d %p %p %[1]v\n", s1, len(s1), cap(s1), &s1, &s1[0])
	// s2 []int len=5 cap=8 [4 5 0 6 7]
	fmt.Printf("%T len=%d cap=%d %p %p %[1]v\n", s2, len(s2), cap(s2), &s2, &s2[0])
	fmt.Println("s1、s2使用的还是一个底层数组？", &s1[0] == &s2[0])
	fmt.Println("~~~~~~~~~~~~~~~~~~")

	// 总结：

	//    | 0 | 0 | 0 | 0 |
	// s1 |           |     len=3 cap=4
	// s2 |               | len=4 cap=4
	// append后
	//    | 0 | 0 | 0 | 0 |
	// s1 |           |         len=3 cap=4
	// ------------------------------------------
	//    | 0 | 0 | 0 | 0 | 0 |
	// s2 |                   | len=5 cap=8

	// s1 使用make创建了一个len=3，cap=4，[]int类型的切片
	// s2 使用append在s1的基础上添加一个元素
	// s1、s2共用总容量为4的底层数组，使用范围不一样，s1使用的是下表0-2，s2使用的是下表0-3
	// s1、s2修改元素时，修改的是底层数组内的元素值
	// 当s2继续追加元素时，newlen=5，超出容量cap=4
	// 将会在内存中重新开辟一段新的连续空间，触发扩容，与s1分道扬镳
	// 因为newlen < 2oldcap, 所以新的cap=8
}

// 2、有一个数组 [1,4,9,16,2,5,10,15]，生成一个新切片，要求新切片元素是数组相邻2项的和
func generateAdjacentSumSlice(array []int) []int {
	result := make([]int, len(array)-1)
	for i := 0; i < len(result); i++ {
		result[i] = array[i] + array[i+1]
	}
	return result
}

// 3、数字重复统计
// 随机产生100个整数
// 数字的范围[-100, 100]
// 降序输出这些生成的数字并打印其重复的次数
func duplicatesNumberCount(count, min, max int) ([]int, map[int]int) {

	// 生成min到max之间的count数量的随机整数
	// numbers := make([]int, 0, count)
	numbers := make([]int, count)
	for i := 0; i < count; i++ {
		// numbers = append(numbers, rand.Intn(max-min+1)+min)
		numbers[i] = rand.Intn(max-min+1) + min
	}

	// 降序
	sort.Sort(sort.Reverse(sort.IntSlice(numbers)))
	// fmt.Println(numbers)

	// 统计数量
	countMap := make(map[int]int)
	for _, num := range numbers {
		countMap[num]++
	}
	// for k, v := range countMap {
	// 	fmt.Printf("num=%d count=%d\n", k, v)
	// }

	return numbers, countMap
}

// 4、简述线性数据结构和哈希表优劣
func question4() {
	// 1. 线性数据结构
	// 线性数据结构的主要特点是有序，线性数据结构分为顺序表和链表。
	// 1) 顺序表
	// 优点：
	// a. 有序。可以按顺序遍历和按索引访问。
	// b. 按索引修改数据方便。
	// 缺点：
	// a. 查找指定元素不便，必须遍历所有数据。
	// b. 通常情况下，大小基本固定，扩展会带来相应的开销。
	// c. 头部、中间插入/删除操作会带来数据的搬迁。

	// 2）链表
	// 优点：
	// a. 有序。
	// b. 可以动态的扩展和缩小。
	// c. 添加、删除元素方便。
	// 缺点：
	// a. 按索引访问时需遍历。
	// b. 查找指定元素不便，必须遍历所有数据。

	// 2. 哈希表
	// 优点：
	// a. 可以快速找到指定key的元素。
	// b. 插入/删除操作相对来说效率较高。
	// c. 灵活性较高。
	// 缺点：
	// a. 内存占用相对较高。
	// b. 哈希冲突。
	// c. 无序。

}
